package com.loyal.camerax.ui.camera

import android.content.res.Configuration
import android.os.Bundle
import android.util.Log
import android.view.OrientationEventListener
import android.view.View
import android.widget.ImageButton
import androidx.camera.core.Camera
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageCapture
import androidx.camera.core.Preview
import androidx.camera.core.impl.ImageOutputConfig
import androidx.camera.core.resolutionselector.ResolutionSelector
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.concurrent.futures.await
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.window.layout.WindowMetrics
import androidx.window.layout.WindowMetricsCalculator
import com.loyal.camerax.config.PhotoConfig
import com.loyal.camerax.impl.PicturePreviewCallback
import com.loyal.camerax.ui.CameraViewModel
import com.loyal.camerax.ui.base.BaseCameraFragment
import com.loyal.camerax.utils.ActivityHelper.hasPermissions
import com.loyal.camerax.utils.FragmentHelper.orientationChanged
import com.loyal.camerax.utils.FragmentHelper.surfaceRotation
import com.loyal.camerax.utils.PermissionHelper
import com.loyal.camerax.utils.aspectRatio
import com.loyal.camerax.utils.hasBackCamera
import com.loyal.camerax.utils.hasFrontCamera
import com.loyal.camerax.utils.observeCameraState
import com.loyal.camerax.utils.removeObservers
import com.loyal.camerax.utils.switchCameraId
import com.loyal.camerax.utils.takePhotoByByteArray
import com.loyal.camerax.utils.zoomAndFocus
import kotlinx.coroutines.launch
import java.io.File
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

open class CameraFragment : BaseCameraFragment(), PicturePreviewCallback {

    private var lensFacing: Int = CameraSelector.LENS_FACING_BACK
    private var preview: Preview? = null
    private var imageCapture: ImageCapture? = null
    private var imageAnalysis: ImageAnalysis? = null
    private var camera: Camera? = null
    private var cameraProvider: ProcessCameraProvider? = null
    private lateinit var windowMetrics: WindowMetrics
    private lateinit var cameraExecutor: ExecutorService
    private var mOrientationListener: OrientationEventListener? = null
    private val cameraViewModel: CameraViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        mOrientationListener = orientationChanged()
    }

    override fun initCamerax() {
        cameraExecutor = Executors.newSingleThreadExecutor()
        windowMetrics =
            WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(requireActivity())

        binding.previewView.post {
            setCameraUI()
            lifecycleScope.launch {
                setUpCamera()
            }
        }
    }

    private suspend fun setUpCamera() {
        cameraProvider = ProcessCameraProvider.getInstance(requireContext()).await()
        val hasBackCamera = cameraProvider.hasBackCamera()
        val hasFrontCamera = cameraProvider.hasFrontCamera()
        binding.cameraPanel.cameraSwitch.isEnabled = hasBackCamera && hasFrontCamera

        lensFacing = when {
            hasBackCamera -> CameraSelector.LENS_FACING_BACK
            hasFrontCamera -> CameraSelector.LENS_FACING_FRONT
            else -> throw IllegalStateException("Back and front camera are unavailable")
        }
        bindCameraUseCases()
    }

    private fun bindCameraUseCases() {
        val metrics = windowMetrics.bounds
        val screenAspectRatio = aspectRatio(metrics.width(), metrics.height())

        val rotation = surfaceRotation()

        val cameraProvider = cameraProvider
            ?: throw IllegalStateException("Camera initialization failed.")
        val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()

        val resolutionSelector = ResolutionSelector.Builder()
            .setAspectRatioStrategy(screenAspectRatio)
            .build()
        // Preview
        preview = Preview.Builder()
            .setResolutionSelector(resolutionSelector)
            .setTargetRotation(rotation)
            .build()

        // ImageCapture
        imageCapture = ImageCapture.Builder()
            .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
            .setResolutionSelector(resolutionSelector)
            .setTargetRotation(rotation)
            .build()

        // ImageAnalysis
        imageAnalysis = ImageAnalysis.Builder()
            .setResolutionSelector(resolutionSelector)
            .setTargetRotation(rotation)
            .build()

        cameraProvider.unbindAll()
        camera?.cameraInfo.removeObservers(viewLifecycleOwner)

        try {
            camera = cameraProvider.bindToLifecycle(
                viewLifecycleOwner, cameraSelector, preview, imageCapture, imageAnalysis
            )
            preview?.setSurfaceProvider(binding.previewView.surfaceProvider)
            camera?.cameraInfo.observeCameraState(viewLifecycleOwner)
            //缩放
            camera.zoomAndFocus(binding.previewView, binding.focusView, cameraExecutor)
        } catch (exc: Exception) {
            Log.e(TAG, "Use case binding failed", exc)
        }
    }

    fun updateViewRotation(
        rotation: Float,
        @ImageOutputConfig.RotationValue targetRotation: Int
    ) {
        binding.settingsPanel.let {
            it.cameraSetting.rotation = rotation
            it.flashSwitch.rotation = rotation
        }
        binding.cameraPanel.let {
            it.cameraSwitch.rotation = rotation
            it.cameraGallery.rotation = rotation
        }

        imageCapture?.targetRotation = targetRotation
        imageAnalysis?.targetRotation = targetRotation
    }

    override fun savePath(): String {
        return ""
    }

    override fun saveName(): String {
        return ""
    }

    override fun takePhoto() {
        val newPath = savePath().ifEmpty {
            requireContext().filesDir.path
        }

        val fileName = saveName().ifEmpty {
            SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US)
                .format(System.currentTimeMillis())
        }
        val imageFile = File(newPath, "$fileName.jpg")

        val isFrontPhoto = CameraSelector.LENS_FACING_FRONT == lensFacing
        val config = PhotoConfig(
            horizontalMirror = isFrontPhoto,
            imageFile = imageFile,
            enableCompress = true,
        )
        imageCapture.takePhotoByByteArray(requireContext(), config, cameraExecutor) {
            if (it) setGalleryThumbnail(imageFile.path)
        }
    }

    override fun onResume() {
        super.onResume()
        cameraViewModel.updateStatus(javaClass.hashCode())
        if (!requireContext().hasPermissions(PermissionHelper.all())) {
            val action = CameraFragmentDirections.actionToPermissions()
            findNavController().navigate(action)
        }
    }

    override fun onPause() {
        super.onPause()
        cameraViewModel.updateStatus(javaClass.hashCode())
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        Log.e(TAG, "onConfigurationChanged: ")
        // Enable or disable switching between cameras
        // Rebind the camera with the updated display metrics
        /*lifecycleScope.launch {
            setUpCamera()
        }*/
    }

    override fun onDestroyView() {
        super.onDestroyView()
        mOrientationListener?.disable()
        cameraExecutor.shutdown()
    }

    override fun updateTorchFlash(flashView: ImageButton, value: String?): Boolean {
        return false
    }

    override fun switchCamera() {
        lensFacing = switchCameraId(lensFacing)
        bindCameraUseCases()
    }

    override fun stateCallback() {
    }

    override fun restart(fragment: Fragment) {
        camera?.cameraInfo?.removeObservers(viewLifecycleOwner)
        cameraProvider?.unbindAll()
        super.restart(fragment)
    }

    companion object {
        private const val TAG = "CameraXBasic"
    }
}